package com.practice.niuke.new_direct_basics.class08;

/**
 * 给定一个整型数组arr， 代表数值不同的纸牌排成一条线。 玩家A和玩家B依次拿走每张纸
 * 牌， 规定玩家A先拿， 玩家B后拿， 但是每个玩家每次只能拿走最左或最右的纸牌， 玩家A
 * 和玩家B都绝顶聪明。 请返回最后获胜者的分数。
 * 【举例】
 * arr=[1,2,100,4]。
 * 开始时， 玩家A只能拿走1或4。 如果开始时玩家A拿走1， 则排列变为[2,100,4]， 接下来
 * 玩家 B可以拿走2或4， 然后继续轮到玩家A...
 * 如果开始时玩家A拿走4， 则排列变为[1,2,100]， 接下来玩家B可以拿走1或100， 然后继
 * 续轮到玩家A...
 * 玩家A作为绝顶聪明的人不会先拿4， 因为拿4之后， 玩家B将拿走100。 所以玩家A会先拿1，
 * 让排列变为[2,100,4]， 接下来玩家B不管怎么选， 100都会被玩家 A拿走。 玩家A会获胜，
 * 分数为101。 所以返回101。
 * arr=[1,100,2]。
 * 开始时， 玩家A不管拿1还是2， 玩家B作为绝顶聪明的人， 都会把100拿走。 玩家B会获胜，
 * 分数为100。 所以返回100。
 */
public class Code08_CardsInLine {

    /**
     * 递归尝试主函数（TODO：标记主函数）
     *
     * @param arr
     * @return
     */
    public static int win1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
    }

    /**
     * 先手函数，在arr[i...j]上先手拿牌能得到的最大分数
     *
     * @param arr 数组
     * @param i   索引
     * @param j   索引
     * @return int
     */
    public static int f(int[] arr, int i, int j) {
        // 只有一张牌了，先手拿牌，当然直接拿走
        if (i == j) {
            return arr[i];
        }
        // 1. 拿arr[i], 那就要在arr[i+1...j]上后手
        // 2. 拿arr[j]，那就要在arr[i...j-1]上后手
        // 然后两者取最大的那个
        return Math.max(
                arr[i] + s(arr, i + 1, j),
                arr[j] + s(arr, i, j - 1)
        );
    }

    /**
     * 后手函数，在arr[i...j]上后手拿牌能得到的最大分数
     *
     * @param arr 数组
     * @param i   索引
     * @param j   索引
     * @return int
     */
    public static int s(int[] arr, int i, int j) {
        // 只有一张牌了，后手拿牌，当然什么也拿不到
        if (i == j) {
            return 0;
        }
        // 1. 对数把arr[i]拿走后, 那在arr[i+1...j]上我就是先手
        // 2. 对手把arr[j]拿走后，那在arr[i...j-1]上我就是先手
        // 对手只会让我拿最小的那个
        return Math.min(
                f(arr, i + 1, j),
                f(arr, i, j - 1)
        );
    }

    public static int win2(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int[][] f = new int[arr.length][arr.length];
        int[][] s = new int[arr.length][arr.length];
        for (int j = 0; j < arr.length; j++) {
            f[j][j] = arr[j];
            for (int i = j - 1; i >= 0; i--) {
                f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
                s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);
            }
        }
        return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);
    }

    public static void main(String[] args) {
        int[] arr = {1, 9, 1};
        System.out.println(win1(arr));
        System.out.println(win2(arr));

    }

}
